/*
 * Copyright 2014 Actelion Pharmaceuticals Ltd., Gewerbestrasse 16, CH-4123 Allschwil, Switzerland
 *
 * This file is part of DataWarrior.
 * 
 * DataWarrior is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 * 
 * DataWarrior is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with DataWarrior.
 * If not, see http://www.gnu.org/licenses/.
 *
 * @author Modest von Korff
 */

package com.actelion.research.chem;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import com.actelion.research.calc.ArrayUtilsCalc;
import com.actelion.research.chem.descriptor.DescriptorHandler;

/**
 * 
 * 
 * ExtendedMoleculeFunctions
 * <p>Copyright: Actelion Ltd., Inc. All Rights Reserved
 * This software is the proprietary information of Actelion Pharmaceuticals, Ltd.
 * Use is subject to license terms.</p>
 * @author Modest von Korff
 * @version 1.0
 * 2005 MvK: Start implementation
 */
public class ExtendedMoleculeFunctions {

	
	public static final int NUMBER_ELEMENTS = 118;
	
	public static final int COLOR_FOR_CENTER_SELECTION = Molecule.cAtomColorRed;
	
	
	public static final int [] arrRGroupsAtomicNo = {142,143,144,129,130,131,132,133,134,135,136,137,138,139,140,141};
	
	public static final String [] arrRGroupsSymbol = {"R1","R2","R3","R4","R5","R6","R7","R8","R9","R10","R11","R12","R13","R14","R15","R16"};
	
	

	
	
	public static void makeSkeleton(StereoMolecule mol) {
		for (int bond=0; bond<mol.getAllBonds(); bond++)
			mol.setBondType(bond, Molecule.cBondTypeSingle);
		for (int atom=0; atom<mol.getAllAtoms(); atom++)
			mol.setAtomicNo(atom, 6);
		}

	public static void analyzeMolecule(StereoMolecule mol){
		int nAtoms = mol.getAtoms();
		
		for (int atom = 0; atom < nAtoms; atom++) {
			if(AtomFunctionAnalyzer.isAcidicOxygen(mol, atom)){
				System.out.println("Acidic oxygen " + atom);
			}
			if(AtomFunctionAnalyzer.isNitroGroupN(mol, atom)){
				System.out.println("Nitro group N " + atom);
			}
			if(AtomFunctionAnalyzer.isMemberOfNitroGroup(mol, atom)){
				System.out.println("Member of group " + atom);
			}
			if(AtomFunctionAnalyzer.isBasicNitrogen(mol, atom)){
				System.out.println("Basic nitrogen " + atom);
			}
			
			
			
		}
		
		System.out.println("Index\tAtomic no\tAtom type");
		for (int indexAtom = 0; indexAtom < mol.getAllAtoms(); indexAtom++) {
			
			long atomType=-1;
			try {
				atomType = AtomTypeCalculator.getAtomType(mol, indexAtom, AtomTypeCalculator.cPropertiesForCLogPCharges);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			System.out.println(indexAtom + "\t" + mol.getAtomicNo(indexAtom) + "\t" + atomType + "\t" + AtomTypeCalculator.toString(atomType));
		}
		
		System.out.println();
	}
	

	
	
	public static String getColorVal2String(Molecule mol, int indexAtom){
		
		int color = mol.getAtomColor(indexAtom);
		
		return getcAtomColor2String(color);
	}
	
	/**
	 * 
	 * @param cAtomColor atom color value from Molecule.
	 * @return
	 */
	public static String getcAtomColor2String(int cAtomColor){
		String sColor = "";
		
		switch (cAtomColor) {
		
		case Molecule.cAtomColorBlack:
			sColor = "black";
			break;
		case Molecule.cAtomColorBlue:
			sColor = "blue";
			break;
		case Molecule.cAtomColorRed:
			sColor = "red";
			break;
		case Molecule.cAtomColorGreen:
			sColor = "green";
			break;
		case Molecule.cAtomColorMagenta:
			sColor = "magenta";
			break;
		case Molecule.cAtomColorOrange:
			sColor = "orange";
			break;
		case Molecule.cAtomColorDarkGreen:
			sColor = "darkGreen";
			break;
		case Molecule.cAtomColorDarkRed:
			sColor = "darkRed";
			break;

		default:
			break;
		}
		
		
		return sColor;
	}

	/**
	 * 
	 * @param mol
	 * @param arrIndexAtom
	 * @param cAtomColor atom color value from Molecule.
	 * @return
	 */
	public static String getColorRecord(Molecule mol, Collection<Integer> liIndexAtom, int cAtomColor){
		
		StringBuilder sb = new StringBuilder();
		
		int cc=0;
		for (int indexAtom : liIndexAtom) {
			
			String sColor = getcAtomColor2String(cAtomColor);
			
			sColor += ":"+indexAtom; 
			
			sb.append(sColor);
			
			if(cc<liIndexAtom.size()-1){
				sb.append(";");
			}
			cc++;
		}
		
		return sb.toString();
	}

	public static int getNumQueryAtoms(ExtendedMolecule mol, int [] arrAtomicNoQuery) {
		int hetero = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			int atomicNo = mol.getAtomicNo(i);
			for (int j = 0; j < arrAtomicNoQuery.length; j++) {
				if(arrAtomicNoQuery[j]==atomicNo){
					hetero++;
					break;
				}
			}
		}
		
		return hetero;
	}
	
	public static int getNumCarbonAtoms(ExtendedMolecule mol) {
		int carbon = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			int atomicNo = mol.getAtomicNo(i);
			if(atomicNo==6) {
				carbon++;
			}
		}
		
		return carbon;
	}
	
	public static int getNumHeteroAtoms(ExtendedMolecule mol) {
		int hetero = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			int atomicNo = mol.getAtomicNo(i);
			if((atomicNo>1) && 
			   (atomicNo != 6) && 
			   (atomicNo <= NUMBER_ELEMENTS)) {
				hetero++;
			}
		}
		
		return hetero;
	}
	
	public static int getNumNitroGroupN(StereoMolecule mol) {
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(AtomFunctionAnalyzer.isNitroGroupN(mol, i)){
				n++;
			}
		}
		
		return n;
	}
	
	public static int getNumAmide(StereoMolecule mol) {
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(AtomFunctionAnalyzer.isAmide(mol, i)){
				n++;
			}
		}
		
		return n;
	}
	
	public static int getNumCarboxy(StereoMolecule mol) {
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(isCarboxyC(mol, i)){
				n++;
			}
		}
		
		return n;
	}
	
	
	public static int getNumAcidicOxygen(StereoMolecule mol) {
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(AtomFunctionAnalyzer.isAcidicOxygen(mol, i)){
				n++;
			}
		}
		
		return n;
	}
	
	public static int getNumBasicNitrogen(StereoMolecule mol) {
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(AtomFunctionAnalyzer.isBasicNitrogen(mol, i)){
				n++;
			}
		}
		
		return n;
	}
	public static int getNumAliphaticRingAtoms(ExtendedMolecule mol, int atomicNoQuery) {
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			int atomicNo = mol.getAtomicNo(i);
			
			if(mol.isRingAtom(i) && atomicNo==atomicNoQuery){
				n++;
			}
		}
		
		return n;
	}
	/**
	 * 
	 * @param mol
	 * @return number of atoms which are not hydrogen.
	 */
	public static int getNumHeavyAtoms(ExtendedMolecule mol) {
		int heavy = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			int atomicNo = mol.getAtomicNo(i);
			if(atomicNo>1 && atomicNo <= NUMBER_ELEMENTS) {
				heavy++;
			}
		}
		
		return heavy;
	}
	
	public static int getNumAromaticAtoms(ExtendedMolecule mol) {

		int n=0;
		for (int i = 0; i < mol.getAtoms(); i++) {
			if(mol.isAromaticAtom(i)){
				n++;
			}
		}
		return n;
	}
	
	public static int getNumArylAmine(StereoMolecule mol) {

		int n=0;
		for (int i = 0; i < mol.getAtoms(); i++) {
			if(AtomFunctionAnalyzer.isArylAmine(mol, i)){
				n++;
			}
		}
		return n;
	}
	
	public static int getNumHeteroAromaticAtoms(ExtendedMolecule mol) {

		int n=0;
		for (int i = 0; i < mol.getAtoms(); i++) {
			if(mol.isRingAtom(i) && mol.isAromaticAtom(i) && mol.getAtomicNo(i)!= 6){
				n++;
			}
		}
		return n;
	}

	public static int getNumIsopropyl(ExtendedMolecule mol) {

		int n=0;
		for (int i = 0; i < mol.getAtoms(); i++) {
			if(mol.isRingAtom(i) && mol.isAromaticAtom(i) && mol.getAtomicNo(i)!= 6){
				n++;
			}
		}
		return n;
	}

	public static int getNumSubstructure(StereoMolecule mol, String idcodeFragment) {
				
		IDCodeParser parser = new IDCodeParser();
		
		StereoMolecule frag = parser.getCompactMolecule(idcodeFragment);
		
		frag.ensureHelperArrays(Molecule.cHelperRings);
		
		SSSearcher ssSearcher = new SSSearcher();
		
		ssSearcher.setMol(frag, mol);
		
		int nMatches = ssSearcher.findFragmentInMolecule();
		
		return nMatches;
	}
	

	
	public static int getBondNo(ExtendedMolecule mol, int atm1,int atm2) {
		for (int bnd=0; bnd<mol.getAllBonds(); bnd++)
			if (((mol.getBondAtom(0,bnd) == atm1)
			  && (mol.getBondAtom(1,bnd) == atm2))
			 || ((mol.getBondAtom(0,bnd) == atm2)
			  && (mol.getBondAtom(1,bnd) == atm1)))
					return bnd;
		return -1;
	}
	
	public static int getBondOrder(ExtendedMolecule mol, int atm1,int atm2) {
		
		int bndno = getBondNo(mol, atm1,atm2);
		if(bndno==-1)
			return -1;
		
		int bndord = mol.getBondOrder(bndno);
		
		return bndord;
	}
	
	public static int getBondType(ExtendedMolecule mol, int atm1,int atm2) {
		
		int bndno = getBondNo(mol, atm1,atm2);
		if(bndno==-1)
			return -1;
		
		int bndtype = mol.getBondType(bndno);
		
		return bndtype;
	}

	public static int getBondParity(ExtendedMolecule mol, int atm1,int atm2) {
		
		int bndno = getBondNo(mol, atm1,atm2);
		if(bndno==-1)
			return -1;
		
		int bndtype = mol.getBondParity(bndno);
		
		return bndtype;
	}

	public static boolean deleteBond(ExtendedMolecule mol, int atm1,int atm2) {
		int bndno = getBondNo(mol, atm1,atm2);
		if(bndno==-1)
			return false;
		
		mol.deleteBond(bndno);
		
		return true;
	}
	
	public static String getBiggestFragmentIDCode(String idCode) {
		
	    StereoMolecule mol = new StereoMolecule();
		new IDCodeParser(false).parse(mol, idCode);
		
        ExtendedMolecule [] frags = mol.getFragments();
        
        String idBiggest = idCode;
        
	    if(frags.length > 1) {
	    	
	    	ExtendedMolecule biggest = mol;
	        int maxAtoms = 0;
	        for (int ii = 0; ii < frags.length; ii++) {
	            if (frags[ii].getAllAtoms() > maxAtoms) {
	                maxAtoms = frags[ii].getAllAtoms();
	                biggest = frags[ii];
	            }
	        }
	        Canonizer canFrag = new Canonizer(biggest);
	        idBiggest = canFrag.getIDCode();
	    }
	    
	    return idBiggest;
	}
	
	public static StereoMolecule getBiggestFragment(StereoMolecule mol) {

		StereoMolecule [] frags = mol.getFragments();
        
		StereoMolecule biggest = mol;
        
	    if(frags.length > 1) {
	    	
	        int maxAtoms = 0;
	        for (int ii = 0; ii < frags.length; ii++) {
	            if (frags[ii].getAllAtoms() > maxAtoms) {
	                maxAtoms = frags[ii].getAllAtoms();
	                biggest = frags[ii];
	            }
	        }
	    }
	    return biggest;
	}

	/**
	 * Replaces all hetero-atoms, except hydrogen, with carbon.
	 * @param m
	 * @return
	 */
	public static StereoMolecule getConverted2CarbonSkeleton(StereoMolecule m){
		
		StereoMolecule skel = new StereoMolecule(m);
		
		skel.ensureHelperArrays(Molecule.cHelperRings);
		
		for (int i = 0; i < skel.getAtoms(); i++) {
			
			if(skel.getAtomicNo(i)>1) {
				skel.setAtomicNo(i, 6);	
			}
		}
		
		skel.ensureHelperArrays(Molecule.cHelperRings);
		
		return skel;

	}
	
	public static Comparator<StereoMolecule> getComparatorAtomsBonds(){
		
		return new Comparator<StereoMolecule>() {

			@Override
			public int compare(StereoMolecule m1, StereoMolecule m2) {
				
				if(m1.getAllAtoms() > m2.getAllAtoms()){
					return 1;
				}else if(m1.getAllAtoms() < m2.getAllAtoms()){
					return -1;
				} else if(m1.getAllBonds() > m2.getAllBonds()){
					return 1;
				} else if(m1.getAllBonds() < m2.getAllBonds()){
					return -1;
				}
				
				return 0;
			}
		};

	}
	
	public static boolean checkBiggestFragmentForUnwanted(StereoMolecule mol, List<Integer> liAtomicNo) {
		boolean bOk=true;
		
        ExtendedMolecule [] frags = mol.getFragments();
        int indexBiggestFrag = 0;
        if(frags.length > 1) {
            int maxAtoms = 0;
            for (int ii = 0; ii < frags.length; ii++) {
                if (frags[ii].getAllAtoms() > maxAtoms) {
                    indexBiggestFrag = ii;
                    maxAtoms = frags[ii].getAllAtoms();
                }
            }
        }
        
        ExtendedMolecule frag = frags[indexBiggestFrag];
        
        for (int i = 0; i < frag.getAllAtoms(); i++) {
			if(liAtomicNo.contains(new Integer(frag.getAtomicNo(i)))) {
				bOk=false;
				break;
			}
		}
		return bOk;
	}
	/**
	 * 
	 * @param mol
	 * @param liAtomicNo
	 * @return true if an unwanted atom is found.
	 */
	public static boolean checkForUnwanted(ExtendedMolecule mol, HashSet<Integer> liAtomicNo) {
		boolean bOk=false;
        
        for (int i = 0; i < mol.getAllAtoms(); i++) {
			if(liAtomicNo.contains(new Integer(mol.getAtomicNo(i)))) {
				bOk=true;
				break;
			}
		}
		return bOk;
	}

	public static boolean checkMolForWanted(ExtendedMolecule mol, HashSet<Integer> liAtomicNo) {
		boolean bOk=true;
        for (int i = 0; i < mol.getAllAtoms(); i++) {
			if(!liAtomicNo.contains(mol.getAtomicNo(i))) {
				bOk=false;
				break;
			}
		}
		return bOk;
	}

	/**
	 * 
	 * @param molecule
	 * @param at index to specify atom in molecule
	 * @param fragment one atom must have the color: COLOR_FOR_CENTER_SELECTION.
	 * @return true if the specified atom in molecule equals the colored atom in fragment.
	 */
	public final static boolean atomAtomSubStrucMatch(StereoMolecule molecule, int at, StereoMolecule fragment) {
		boolean bMatch = false;
		
		SSSearcher sss = new SSSearcher();
		sss.setMol(fragment, molecule);
		int numFrags = sss.findFragmentInMolecule(SSSearcher.cCountModeOverlapping, SSSearcher.cMatchAromDBondToDelocalized);
		if(numFrags == 0)
			return false;
		
		List<int []> liAtomLists = sss.getMatchList();
		ArrayUtilsCalc.removeDoubletsIntOrderIndepend(liAtomLists);
		
		int atomMarkedInQuery = -1;
		for (int i = 0; i < fragment.getAllAtoms(); i++) {
			if(fragment.getAtomColor(i)== COLOR_FOR_CENTER_SELECTION){
				atomMarkedInQuery = i;
			}
		}
		
		for (Iterator<int []> iter = liAtomLists.iterator(); iter.hasNext();) {
			int[] arrInd = (int[]) iter.next();
			if(arrInd[atomMarkedInQuery] == at){
				bMatch = true;
				break;
			}
		}
		
		return bMatch;
		
	}
	
	public static double [][] getDistanceArray(ExtendedMolecule mol) {
		
		double arr[][]= new double[mol.getAllAtoms()][mol.getAllAtoms()];
		
		for (int i = 0; i < arr.length; i++) {
			for (int j = i+1; j < arr.length; j++) {
				double dx = mol.getAtomX(i) - mol.getAtomX(j);
				double dy = mol.getAtomY(i) - mol.getAtomY(j);
				double dz = mol.getAtomZ(i) - mol.getAtomZ(j);
				double v= Math.sqrt(dx*dx+dy*dy+dz*dz);
				arr[i][j] = v;
				arr[j][i] = v;
			}
		}
		
		return arr;
	}
	
	
	public final static int getTopologicalDistance(ExtendedMolecule mol, int at1, int at2) {
		int dist = 0;
	
		if(at1 == at2)
			return 0;
		
		List<Integer> liExamine = new ArrayList<Integer>();
		List<Integer> liSphere = new ArrayList<Integer>();
		List<Integer> liVisited = new ArrayList<Integer>();
		
		liExamine.add(new Integer(at1));
		
		liSphere.add(new Integer(1));
		
		boolean bFound = false;
		while(!liExamine.isEmpty()) {
			dist = ((Integer)liSphere.remove(0)).intValue();
			int indAtCenter = ((Integer)liExamine.remove(0)).intValue();
			liVisited.add(new Integer(indAtCenter));
			int numJNeighbors = mol.getAllConnAtoms(indAtCenter);
			for (int i = 0; i < numJNeighbors; i++) {
				int indAtNeighb = mol.getConnAtom(indAtCenter, i);
				if(indAtNeighb == at2) {
					bFound = true;
					break;
				}
				if(!liVisited.contains(new Integer(indAtNeighb))) {
					liExamine.add(new Integer(indAtNeighb));
					liSphere.add(new Integer(dist + 1));
				}
			}
			if(bFound)
				break;
		}
		
		if(!bFound)
			dist = -1;
		
		return dist;
	}
	
	/**
	 * 
	 * @param mol
	 * @param atm index of atom
	 * @return false if atom is not C or if one of the neighbours is N, O, F, S, P or Cl.
	 */
	public static boolean isAliphaticAtom(StereoMolecule mol, int atm) {
		boolean aliphatic = true;
		
		if(mol.getAtomicNo(atm)!=6)
			return false;
		
		int nConn = mol.getAllConnAtoms(atm);
		
		for (int i = 0; i < nConn; i++) {
			int atmConn = mol.getConnAtom(atm, i);
			
			int atomicNoConn = mol.getAtomicNo(atmConn);
			
			if((atomicNoConn == 7) || 
			   (atomicNoConn == 8) ||
			   (atomicNoConn == 9) ||
			   (atomicNoConn == 15) ||
			   (atomicNoConn == 16) ||
			   (atomicNoConn == 17)) {
				aliphatic = false;
				break;
			}
		}
		
		return aliphatic;
	}
	
	public static boolean isAcceptor(StereoMolecule mol, int atom) {
		boolean acceptor = true;

		if(mol.getAtomicNo(atom)!=8 && mol.getAtomicNo(atom)!=7)
			return false;
		
		return acceptor;
	}
	
	public static boolean isDonor(StereoMolecule mol, int atom) {
		boolean donor = true;

		if(mol.getAtomicNo(atom)!=8 && mol.getAtomicNo(atom)!=7)
			return false;
		
		if(mol.getAllHydrogens(atom)==0){
			return false;
		}

		return donor;
	}
	
	
	public static boolean isCarbonTwoValencesMinimum(StereoMolecule mol) {
		
		boolean twoValencesMin = true;
		
		int atoms = mol.getAtoms();
		
		
		for (int i = 0; i < atoms; i++) {
			
			int atomicNo = mol.getAtomicNo(i);
			
			if(atomicNo==6){
				
				int connected = mol.getConnAtoms(i);
				
				if(connected < 2){
					twoValencesMin = false;
					break;
				}
			}
		}
		
		return twoValencesMin;
	}
	
	public static boolean isCarbonOnlyConnected2Hetero(StereoMolecule mol) {
		
		boolean hetero = true;
		
		int atoms = mol.getAtoms();
		
		outer:
		for (int i = 0; i < atoms; i++) {
			
			int atomicNo = mol.getAtomicNo(i);
			
			if(atomicNo==6){
				
				int connected = mol.getConnAtoms(i);
				
				for (int j = 0; j < connected; j++) {
					
					int indexConnecetd = mol.getConnAtom(i, j);
					
					if(mol.getAtomicNo(indexConnecetd)==6){
						hetero = false;
						break outer;
					}
					
				}
				
				if(mol.isRingAtom(i)){
					hetero = true;
					break;
				}
			}
		}
		
		return hetero;
	}
	
	public static boolean isRingInMolecule(StereoMolecule mol) {
		
		boolean ring = false;
		
		int atoms = mol.getAtoms();
		
		for (int i = 0; i < atoms; i++) {
			
			if(mol.isRingAtom(i)){
				ring = true;
				break;
			}
		}
		
		return ring;
	}
	
	/**
	 * 
	 * @param mol
	 * @return true if all atoms are in a ring.
	 */
	public static boolean isRingExclusively(StereoMolecule mol) {
		
		boolean ring = true;
		
		int atoms = mol.getAtoms();
		
		for (int i = 0; i < atoms; i++) {
			
			if(!mol.isRingAtom(i)){
				ring = false;
				break;
			}
		}
		
		return ring;
	}
	
	/**
	 * 
	 * @param mol
	 * @param atom
	 * @return true for cyano and iso-cyano.
	 */
	public static boolean isCyanoN(StereoMolecule mol, int atom) {
		
		if(mol.getAtomicNo(atom)!= 7){
			return false;
		}
		

		int nConn = mol.getConnAtoms(atom);
		
		if(nConn != 1){
			return false;
		}
		
		int indexConn = mol.getConnAtom(atom, 0);
		
		int bond = mol.getBond(atom, indexConn);
		
		if(mol.getBondOrder(bond) != 3){
			return false;
		}
				
		return true;
	}
	
	public static boolean isThioEther(StereoMolecule mol, int atom) {
		
		if(mol.getAtomicNo(atom)!= 16){
			return false;
		}
		

		int nConn = mol.getConnAtoms(atom);
		
		if(nConn != 2){
			return false;
		}
		
		boolean thio = true;
		for (int i = 0; i < nConn; i++) {
			
			int indexConn = mol.getConnAtom(atom, i);
			
			if(mol.getAtomicNo(indexConn) != 6){
				thio=false;
				break;
			}
			
		}
				
		return thio;
	}
	
	/**
	 * 
	 * @param mol
	 * @param atom
	 * @return true if atom is S and at let one attached atom is O.
	 */
	public static boolean isSulfoxyGroup(StereoMolecule mol, int atom) {
		
		if(mol.getAtomicNo(atom)!= 16){
			return false;
		}
		
		int nConn = mol.getConnAtoms(atom);
				
		boolean oxy = false;
		for (int i = 0; i < nConn; i++) {
			
			int indexConn = mol.getConnAtom(atom, i);
			
			if(mol.getAtomicNo(indexConn) == 8){
				oxy=true;
				break;
			}
			
		}
				
		return oxy;
	}
	
	/**
	 * Counts cyano and iso-cyano
	 * @param mol
	 * @return
	 */
	public static int getNumCyanoGroups(StereoMolecule mol){
		
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(isCyanoN(mol, i)){
				n++;
			}
		}
		
		return n;

	}
	

	public static int getNumAlcoholicOxygen(StereoMolecule mol){
		
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(isAlcoholicOxygen(mol, i)){
				n++;
			}
		}
		
		return n;

	}
	
	public static int getNumThioEther(StereoMolecule mol){
		
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(isThioEther(mol, i)){
				n++;
			}
		}
		
		return n;

	}
	
	public static int getNumSulfOxyGroups(StereoMolecule mol){
		
		int n = 0;
		
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			
			if(isSulfoxyGroup(mol, i)){
				n++;
			}
		}
		
		return n;

	}
	
	public static boolean isAlcoholicOxygen(StereoMolecule mol, int atom) {
		
		if (mol.getAtomicNo(atom) != 8 || mol.isAromaticAtom(atom))
			return false;

		int nConnected = mol.getConnAtoms(atom);
		
		if(nConnected != 1){
			
			return false;
		}
		
		int indConn = mol.getConnAtom(atom, 0);
		
		if(mol.getAtomicNo(indConn) != 6){
			return false;
		}

		if(AtomFunctionAnalyzer.getNegativeNeighbourCount(mol, indConn) > 1){
			return false;
		}
		
		return true;
	}
	
	public static boolean isCarboxyC(StereoMolecule mol, int atom) {
		
		if (mol.getAtomicNo(atom) != 6 || mol.getAtomPi(atom) != 0)
			return false;

		boolean carboxyO = false;
		
		boolean etherO = false;
		
		for (int i=0; i<mol.getConnAtoms(atom); i++) {
			
			int conn = mol.getConnAtom(atom, i);
			
			if (mol.getAtomicNo(conn) == 6) {
				
				int bond = mol.getBond(atom, conn);
				
				int bo = mol.getBondOrder(bond);
				
				if(bo==1){
					etherO=true;
				} else if(bo==2){
					carboxyO=true;
				}
				
			}
					
		}

		boolean carboxyC=false;
		
		if(carboxyO && etherO){
			carboxyC=true;
		}
		
		return carboxyC;
	}

	public static int replaceAtoms(ExtendedMolecule [] arr, int atnoOrig,int atnoRpl) {
		int cc=0;
		for (int i = 0; i < arr.length; i++) {
			cc+=replaceAtoms(arr[i], atnoOrig,atnoRpl);
		}
		return cc;
	}
	
	public static int replaceAtoms(ExtendedMolecule mol, int atnoOrig,int atnoRpl) {
		int cc=0;
		for (int i = 0; i < mol.getAllAtoms(); i++) {
			if(mol.getAtomicNo(i)==atnoOrig) {
				mol.setAtomicNo(i, atnoRpl);
				cc++;
			}
		}
		return cc;
	}


	
	
	
	/**
	 * Removes all molecules that are a substructure of one of the molecules in the input list.
	 * @param liInput
	 * @return
	 */
	public static final LinkedList<StereoMolecule> removeSubStructures(List<StereoMolecule> liInput){
		SSSearcher sss = new SSSearcher();
		
		LinkedList<StereoMolecule> li = new LinkedList<StereoMolecule>(liInput);
		
		for (int i = li.size()-1; i >= 0; i--) {
			
			StereoMolecule mol1 = li.get(i);
			
			for (int j = 0; j < li.size(); j++) {
				if(i!=j){
				
					StereoMolecule mol2 = li.get(j);
					
					if(mol1.getAtoms()<mol2.getAtoms()){
						sss.setMol(mol1, mol2);
						
						if(sss.isFragmentInMolecule()){
							li.remove(i);
							break;
						}
					}
				}
			}
		}
		
		return li;
	}

	/**
	 * Deletes the substructures, if found, in the molecule. 
	 * Starts with the first structure in the list. 
	 * Helper arrays are ensured.
	 * @param mol
	 * @param liFragment
	 * @return
	 */
	public static StereoMolecule removeSubstructuresFromMolecule(StereoMolecule mol, List<StereoMolecule> liFragment){
		
		StereoMolecule molReduced = new StereoMolecule(mol);
		
		molReduced.ensureHelperArrays(Molecule.cHelperRings);
		
		SSSearcher sss = new SSSearcher();
		
		HashSet<Integer> hsIndexMatchingAtoms = new HashSet<Integer>();
		
		for (StereoMolecule frag : liFragment) {
			
			sss.setMol(frag, molReduced);
			
			if(sss.findFragmentInMolecule()>0){
				
				List<int[]> liMatch = sss.getMatchList();
				
				hsIndexMatchingAtoms.clear();
				
				for (int[] arrMatch : liMatch) {
					
					for (int i = 0; i < arrMatch.length; i++) {
						hsIndexMatchingAtoms.add(arrMatch[i]);
					}
				}

				int [] arrIndexMatchingAtomsUnique = ArrayUtilsCalc.toIntArray(hsIndexMatchingAtoms);
				
				molReduced.deleteAtoms(arrIndexMatchingAtomsUnique);
								
				molReduced.ensureHelperArrays(Molecule.cHelperRings);
								
			}
		}
		
		
		return molReduced;
	}
	
	public static StereoMolecule removeSubstructureFromMolecule(StereoMolecule mol, StereoMolecule frag){
		
		StereoMolecule molReduced = new StereoMolecule(mol);
		
		molReduced.ensureHelperArrays(Molecule.cHelperRings);
		
		SSSearcher sss = new SSSearcher();
		
		
					
		sss.setMol(frag, molReduced);
			
		if(sss.findFragmentInMolecule()>0){
				
			List<int[]> liMatch = sss.getMatchList();
			
			
			if(liMatch.size() > 1){
				throw new RuntimeException("Fragment found more than once!");
				
			} if(liMatch.size() == 1){
				
				int [] arrMatchIndex = liMatch.get(0);  
				
				molReduced.deleteAtoms(arrMatchIndex);
				
				molReduced.ensureHelperArrays(Molecule.cHelperRings);
							
//				boolean [] arrMatchingAtom = new boolean [molReduced.getAtoms()];
//				
//				
//				
//				for (int i = 0; i < arrMatchIndex.length; i++) {
//					arrMatchingAtom[arrMatchIndex[i]]=true;
//				}
//				
//				int indexRGroup = 1;
//				
//				for (int i = 0; i < arrMatchIndex.length; i++) {
//					
//					int atMatch = arrMatchIndex[i];
//					
//					int nConnected = molReduced.getConnAtoms(atMatch);
//					
//					for (int j = 0; j < nConnected; j++) {
//						
//						int atConnected = molReduced.getConnAtom(atMatch, j);
//						
//						if(arrMatchingAtom[atConnected]){
//							continue;
//						}
//						
//						int bond = molReduced.getBond(atMatch, nConnected);
//						
//						molReduced.deleteBond(bond);
//						
//						int atNoRGroup = getAtomicNoRGroup(indexRGroup++);
//						
//						int indexAtRGroup = molReduced.addAtom(atNoRGroup);
//						
//						molReduced.addBond(atConnected, indexAtRGroup, Molecule.cBondTypeSingle);
//						
//						molReduced.ensureHelperArrays(Molecule.cHelperNeighbours);
//						
//					}
//					
//				}
//				
				
				
			}
		
		
							
		}
		
		
		
		return molReduced;
	}
	
	public static StereoMolecule removeWildcards(StereoMolecule mol){
		
		StereoMolecule molReduced = new StereoMolecule(mol);
		
		molReduced.ensureHelperArrays(Molecule.cHelperRings);
		
		for (int i = molReduced.getAtoms()-1; i >= 0; i--) {
			
			if(molReduced.getAtomicNo(i)==0){
				molReduced.deleteAtom(i);
			}
			
		}
		
		molReduced.ensureHelperArrays(Molecule.cHelperRings);
		
		return molReduced;
	}

	
	public static void setColorMCS2Molecule(StereoMolecule mol, StereoMolecule molMCS){
		
		for (int i = 0; i < mol.getAtoms(); i++) {
			mol.setAtomColor(i, Molecule.cAtomColorBlack);
		}
		
		SSSearcher sss = new SSSearcher();
		
		sss.setMol(molMCS, mol);
		
		if(sss.findFragmentInMolecule()>0){
			List<int[]> liMatch = sss.getMatchList();
			
			int [] arrIndexMatch = liMatch.get(0);
			
			for (int i = 0; i < arrIndexMatch.length; i++) {
				mol.setAtomColor(arrIndexMatch[i], Molecule.cAtomColorBlue);
			}
			
		}
		
	}
	
	public static void setColorMolecule(StereoMolecule mol, int [] arrIndexMatch){
		
		for (int i = 0; i < mol.getAtoms(); i++) {
			mol.setAtomColor(i, Molecule.cAtomColorBlack);
		}
			
		for (int i = 0; i < arrIndexMatch.length; i++) {
			mol.setAtomColor(arrIndexMatch[i], Molecule.cAtomColorBlue);
		}
	}
	
	/**
	 * 
	 * @param mol
	 * @param arrIndexBonds
	 * @param color i.e. Molecule.cAtomColorBlue
	 */
	public static void setColorMoleculeFromBondIndex(StereoMolecule mol, int [] arrIndexBonds, int color){
		
		for (int i = 0; i < arrIndexBonds.length; i++) {
			
			final int indexAtom1 = mol.getBondAtom(0, arrIndexBonds[i]);
			
			final int indexAtom2 = mol.getBondAtom(1, arrIndexBonds[i]);
			
			mol.setAtomColor(indexAtom1, color);
			mol.setAtomColor(indexAtom2, color);

		}
	}
	
	public static void setCoordinatesNull(StereoMolecule mol){
		for (int i = 0; i < mol.getAtoms(); i++) {
			mol.setAtomX(i,0);
			mol.setAtomY(i,0);
			mol.setAtomZ(i,0);
		}
	}

	/**
	 * Returns the colors vector for the substructure in mol.
	 * @param mol
	 * @param frag
	 * @param atomColor Molecule.cAtomColor
	 * @return
	 */
	public static String getColorVectorSubstructure(StereoMolecule mol, StereoMolecule frag, int atomColor){
		
		SSSearcher sss = new SSSearcher();
		
		sss.setMol(frag, mol);
		
		HashSet<Integer> hsAtomIndex = new HashSet<Integer>();
		
		if(sss.findFragmentInMolecule()>0){
			List<int[]> liMatch = sss.getMatchList();
			
			
			int[] arr = liMatch.get(0);
			for (int i = 0; i < arr.length; i++) {
				hsAtomIndex.add(arr[i]);
			}
			
//			for (int[] arr : liMatch) {
//				for (int i = 0; i < arr.length; i++) {
//					hsAtomIndex.add(arr[i]);
//				}
//			}
		}
		
		String sAtomColor = ExtendedMoleculeFunctions.getColorRecord(mol, hsAtomIndex, atomColor);
		
		return sAtomColor;
		
	}
	
	public static float getSimilarity(StereoMolecule m1, StereoMolecule m2, DescriptorHandler dh){
	
		Object d1 = dh.createDescriptor(m1);
		Object d2 = dh.createDescriptor(m2);
		
		return dh.getSimilarity(d1, d2);
	}
	
	/**
	 * Starts with 1 and goes until 16
	 * @return
	 */
	public static int getAtomicNoRGroup(int r){
		
		if(r > arrRGroupsAtomicNo.length){
			throw new RuntimeException("");
		}
		
		return arrRGroupsAtomicNo[r-1];
		
	}
	

}

